ヘッダーをスキップ
Oracle TimesTen In-Memory Database Java開発者およびリファレンス・ガイド
リリース6.0
B25766-01
  目次へ
目次
索引へ
索引

前へ
前へ
次へ
次へ
 

Javaアプリケーションのチューニング

この項では、TimesTen用のJavaアプリケーションをチューニングする場合の一般的な方針について説明します。

この章の内容は次のとおりです。

自動コミット・モードの無効化

デフォルトでは自動コミットは有効になっており、文を実行するとコミットが強制実行されます。文を実行するたびにコミットを実行すると、パフォーマンスに悪影響を及ぼす可能性があります。パフォーマンスを重要視するアプリケーションの場合は、自動コミットを無効にすることをお薦めします。「自動コミットの設定」を参照してください。

SYS.MONITOR表のXACT_COMMITS列は、トランザクションのコミット数を示します。

アプリケーションで明示的なコミットを指定しなかった場合、メモリー、ロックなどの重要なリソースを不必要に使い果たしてしまう可能性があります。すべてのアプリケーションで、定期的なコミットを実行する必要があります。

タイムアウト時間の選択

ロックのタイムアウト時間を変更するには、組込みプロシージャttLockWaitを使用します。デフォルトでは、接続は10秒待機してロックを設定します。一部のアプリケーションでは、この間隔が長すぎる場合または短すぎる場合があります。

競合の軽減

データ・ストアで競合が発生すると、アプリケーションのパフォーマンスが大幅に低下する可能性があります。

アプリケーションでの競合を軽減するには、次の手順を実行します。

最適なロック方法の選択

複数の接続で同時にデータ・ストアにアクセスする場合、TimesTenは、ロックを使用して様々なトランザクションが明示的に分離されて処理されるようにします。TimesTenでは、『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のトランザクションの管理およびリカバリに関する項で説明されている分離レベルがサポートされています。また、データ・ストア・レベル、表レベルおよび行レベルのロック・レベルがサポートされています。LockLevel接続属性を使用すると、データ・ストア・レベルと行レベルのロックのいずれを使用するかを指定できます。表ロックを使用するかどうかを示すオプティマイザ・ヒントを設定するには、ttOptSetFlagプロシージャを使用します。デフォルトのロック粒度は行ロックです。

適切なロック・レベルの選択

データ・ストアでの競合が非常に少ない場合は、表レベル・ロックを使用します。これによって、パフォーマンスが改善され、デッドロックの可能性も低くなります。通常、トランザクションが短い場合または接続が少ない場合(あるいはその両方の場合)は、データ・ストアでの競合が少なくなります。この場合、トランザクションが重複する可能性は低くなります。

また、表レベル・ロックは、文が表のほぼすべての行にアクセスする場合にも有効です。このような文には、1つのトランザクションで実行される問合せ、更新、削除、複数の挿入などがあります。

TimesTenでは、シリアライズ可能分離レベルでのみ表ロックが使用されます。アプリケーションにおいて他の分離レベルで表ロックが指定される場合は、表レベル・ロックが上書きされて行ロックが使用されます。ただし、オプティマイザ計画では、表レベル・ロック・ヒントが表示される可能性があります。

データ・ストア・レベル・ロックでは表レベル・ロックより同時実行性が制限されるため、通常、データ・ストア・レベル・ロックは、同時実行性が必要でない場合にバルク・ロードなどの初期化処理でのみ有効です。データ・ストア・レベル・ロックを使用すると、スループットは低下しますが、行レベル・ロックまたは表レベル・ロックよりレスポンス時間が向上します。

通常、同じ行にアクセスする必要がほとんどない同時トランザクションが多数存在する場合は、行レベル・ロックを使用することを薦めします。

適切な分離レベルの選択

行レベル・ロックを使用している場合、アプリケーションでは、トランザクションをシリアライズ可能またはコミット読取りの分離レベルで実行できます。デフォルトの分離レベルはコミット読取りです。Isolation接続属性を使用すると、これらの分離レベルのいずれかを新しい接続に指定できます。

シリアライズ可能トランザクション分離レベルで実行すると、トランザクションの処理中、TimesTenですべてのロックが保持されるため、次の処理が実行されます。

適切なロギング・オプションの選択

TimesTen Data Managerは、トランザクションに関するログをディスクに保持することで、データ・ストア・トランザクションを永続的にします。ログ・レコードは、トランザクションのコミット時に書き込まれます。非永続コミットまたはディスクレス・ロギングを指定しないかぎり、コミットのたびにディスクへの書込みが発生します(「永続コミットの適切な使用」を参照)。

デフォルトのロギング設定(DurableCommits=1)では、他の同時接続に対するログI/Oはグループ・コミットと呼ばれる方法で軽減されますが、レスポンス時間が長くなる場合があります。ログI/Oは、ディスクへの書込みが行われないようにできる十分な同時実行性がない場合、スループットにも影響を及ぼします。SYS.MONITOR表のLOG_FORCES列には、ディスクにログをフラッシュした回数が記録されます。ディスクにログをフラッシュする回数が多すぎると、ログ・ファイルへの書込みと、チェックポイント・ファイルへの書込みとの間で、I/Oの競合が発生する可能性があります。LogDir接続属性を使用すると、ログ・ファイルに別のI/Oパスを指定できます。

ディスクレス・ロギングを設定した場合、ログ・バッファの大きさが十分でないと、ログ・ファイルは生成されず、トランザクション全体がロールバックされる可能性があります。したがって、ディスクレス・ロギングを使用している場合、トランザクションの永続性はTimesTenでは保証されません。アプリケーションでは、チェックポイントを使用して永続性を保証できます。通常、ディスクレス・ロギングを使用するとトランザクションの強制終了が多発する可能性があるため、ディスクレス・ロギングを使用する場合は注意が必要です。

一部のアプリケーションでは、トランザクションの消失が許容される場合があります。たとえば、システムまたはアプリケーションで障害が発生しても、別のソースからデータを再生成できる場合があります。また、必要に応じてデータを保存するために効果的な間隔でチェックポイント処理をアプリケーションで実行できる場合があります。これらの場合、データ・ストアへの接続時に、ロギングを無効に設定することをお薦めします(『Oracle TimesTen In-Memory Database APIおよびSQLリファレンス・ガイド』のデータ・ストア属性に関する項を参照)。ただし、これによって原子性が失われる場合があります(『Oracle TimesTen In-Memory Databaseオペレーション・ガイド』のトランザクションの管理およびリカバリに関する項を参照)。

また、ログは、エラー処理中トランザクションのロールバックで使用されます。また、アプリケーションまたはシステムでエラーが発生した場合にトランザクションの再実行に使用されます。ロギングを無効にすると、ほとんどの文は原子的に実行されますが、トランザクション全体はロールバックできません。原子的に実行されなかった文の場合は、エラーが返されます。アプリケーションが開発段階である場合、またはなんらかの理由でロールバックが多発している場合、ロギングを無効にすると、これらのエラーが発生する可能性があります。これらのエラーが発生すると、データ・ストアの一貫性が失われる場合があります。この場合、ディスクレス・ロギングまたはディスクへのロギングを使用することをお薦めします。非永続コミットの使用方法については、「永続コミットの適切な使用」を参照してください。

次に、ロギングに関する追加情報を示します。

文の事前準備

文を複数回実行する場合は、アプリケーションで文を事前に準備する必要があります(「SQL文の準備」を参照)。準備した文が、準備によって保持されたロックを解放し、問合せ計画を持続するためにコミットされていることを確認してください。

同じ文を複数回生成し、その生成のたびに異なる値を検索するアプリケーションが存在する場合は、パラメータ化した文を使用して、コンパイル時間を減らします。たとえば、アプリケーションで次のような文が生成されるとします。

SELECT A FROM B WHERE C = 10  
SELECT A FROM B WHERE C = 15  
 

これらの文は、次の単一の文に置き換えることができます。

SELECT A FROM B WHERE C = ?

TimesTenは、コミットされると自動的に準備済のコマンドを共有します。したがって、コマンドの実行を準備するためのアプリケーションによるリクエストは、準備済バージョンのコマンドがすでにシステムに存在する場合は非常に高速に処理されます。また、同じコマンドの*Statement.execute*に対して繰り返しリクエストを行う場合は、事前準備済バージョンのコマンドを共有することによって、準備によるオーバーヘッドを回避できる場合があります。

TimesTenで準備済の文を共有できる場合でも、パフォーマンス上の理由から、パラメータ化された文を使用することをお薦めします。パラメータ化された文を使用すると、文の共有によって実行できる処理での準備で発生するオーバーヘッドを軽減できます。

不要な準備処理の回避

SQL文の準備は高コストの処理であるため、アプリケーションで、Connection.prepareStatementメソッドのコール回数を最小にする必要があります。ほとんどのアプリケーションは、接続を開始した時点でコマンド・セットを準備し、接続中はこのセットを使用します。この方法は、接続が長く、数百または数千のトランザクションで構成されている場合に有効です。接続が比較的短い場合は、すべてのスレッドまたはプロセスのためにコマンドを準備して実行する長時間の接続を確立することをお薦めします。この場合、通信のオーバーヘッドと準備のオーバーヘッドのバランスを考慮する必要があります。これは、各アプリケーションに対して調べることができます。準備済の文は、接続がクローズされると無効になります。

複数の文を実行するためのバッチ更新機能の使用

TimesTen JDBCドライバでは、JDBCオブジェクトのStatementPreparedStatementに、addBatchメソッド、clearBatchメソッドおよびexecuteBatchメソッドがサポートされています。これらのAPIをアプリケーションで使用すると、Statementオブジェクトを使用して複数のSQL更新操作を実行したり、PreparedStatementオブジェクトを使用して準備済の文に関連付けられた複数のパラメータのセットを実行する場合のパフォーマンスを向上させることができます。

Statementオブジェクトの場合、バッチは一連のSQL書込み操作文で構成されています。結果セットを返す文は、バッチでは使用できません。addBatch()メソッドをコールすると、バッチにSQL書込み操作文が追加されます。バッチに関連付けられている一連のSQL文は、executeBatch()メソッドによって実行されます。次に例を示します。

// turn off autocommit 
conn.setAutoCommit(false); 
Statement stmt = conn.createStatement(); 
stmt.addBatch("INSERT INTO employees VALUES (1000, 'Joe
Jones')"); 
stmt.addBatch("INSERT INTO departments VALUES (260, 'Shoe')"); 
stmt.addBatch("INSERT INTO emp_dept VALUES (1000, 260)"); 
// submit a batch of update commands for execution 
int[] updateCounts = stmt.executeBatch(); 
conn.commit (); 
 

PreparedStatementオブジェクトの場合、バッチは一連の準備済の文の入力パラメータで構成されています。準備済の文のパラメータは、setXXXコールの後にaddBatchコールを実行することによってバッチに追加されます。バッチは、executeBatchメソッドを介して実行されます。次に例を示します。

// turn off autocommit conn.setAutoCommit(false); PreparedStatement stmt = conn.prepareStatement( "INSERT INTO employees VALUES (?, ?)"); // first set of parameters stmt.setInt(1, 2000); stmt.setString(2, "Kelly Kaufmann"); stmt.addBatch(); // second set of parameters stmt.setInt(1, 3000); stmt.setString(2, "Bill Barnes"); stmt.addBatch(); // submit the batch for execution int[] updateCounts = stmt.executeBatch(); conn.commit ();

JDBCバッチ更新機能の使用方法の詳細は、StatementおよびオブジェクトのJava Platform API仕様を参照してください。

TimesTenデータ行のバルク・フェッチ

TimesTenで提供されている拡張を使用すると、アプリケーションで複数のデータ行をフェッチできます。大量のTimesTenデータを取得するアプリケーションの場合、複数行のフェッチによってパフォーマンスを大幅に向上させることができます。ただし、コミット読取り分離モードが使用される場合は、アプリケーションですべてのデータが取得されるまで、取得されるすべての行にロックが設定されるため、同時実行性は低下します。この機能の詳細は、「複数のデータ行のフェッチ」を参照してください。

トランザクションの適切なサイズ設定

デフォルトでログ・レコードを生成する各トランザクション(たとえば、INSERT、DELETEまたはUPDATEを実行するトランザクション)では、トランザクションのコミット時にディスクへの書込みが行われます(「適切なロギング・オプションの選択」を参照)。ディスクI/Oは、レスポンス時間に影響を及ぼします。また、グループ・コミットの有効性によっては、スループットに影響を及ぼす場合があります。

パフォーマンスを重視するアプリケーションの場合は、コミット時に不要なディスクへの書込みが行われないようにする必要があります。パフォーマンス解析ツールを使用して、アプリケーションでディスクへの書込みにかかる時間を(CPU時間と比較して)測定します。I/Oが過剰であると思われる場合は、次の2つの手順を実行してコミット時に書込みが行われないようにできます。

永続コミットの適切な使用

デフォルトでは、TimesTenのトランザクションをコミットするたびに、ディスクへの書込みが発生します。これによって、システムまたはアプリケーションで障害が発生した場合でも、コミット済のトランザクションは失われなくなります。非永続コミットを使用すると、これらのディスクへの書込みの一部またはすべてが行われないようにすることができます。非永続コミットでは、トランザクション・ログをディスクに書き込むことを除き、永続コミットで実行されるすべての処理が実行されます。ロックは解除され、カーソルはクローズされますが、ディスクへの書込みは実行されません。

注意: 一部のコントローラまたはドライバは、コントローラのキャッシュ・メモリーにデータを書き込むのみか、または書込みが終了したことがオペレーティング・システムに通知された後でのみディスクにデータを書き込みます。この場合、停電が発生すると、永続コミットしたと思われていた一部の情報が失われる可能性があります。このようなデータの損失を回避するには、終了通知の前に記録メディアに書き込むようディスクを構成するか、または無停電電源装置(UPS)を使用します。

非永続コミットのメリットは、レスポンス時間を減らし、スループットを向上させることができることです。デメリットは、システムで障害が発生した場合に、一部のトランザクションが失われる可能性があることです。アプリケーションでは、定期的に永続コミットまたはチェックポイントを実行してディスクへのロギングを強制実行することによって、失われる可能性があるデータの量を減らすことができます。また、TimesTen自体でも、内部バッファが満杯になった場合にログをディスクに定期的にフラッシュしているため、失われるデータの量が制限されます。

トランザクションは、接続ごとに永続的にするか、または永続性遅延モードを設定できます。アプリケーションで、ttDurableCommitプロシージャをコールして、特定のトランザクションの永続コミットを強制実行できます。

非永続コミットを使用しないアプリケーションでは、書込みおよびフラッシュのかわりに同期書込みを使用することをお薦めします。同期化書込みを有効にするには、LogFlushMethod=2を設定します。

SYS.MONITOR表のXACT_D_COMMITS列は、永続コミットを実行したトランザクションの数を示します。

ResultSet.getStringメソッドの限定的使用

Javaの文字列は不変的であるため、ResultSet.getStringは、(基礎となるC文字列からUnicode文字列への変換に加えて、)コールされるたびに新しい文字列用の領域を確保する必要があります。そのため、ResultSet.getStringは、JDBCにおいて最もコストのかかる処理の1つといえます。

また、必要時以外は、ResultSet.getStringをコールして、ByteやIntegerなどの基本的な数値型を取得しないでください。たとえば、整数列の場合、ResultSet.getIntをコールする方が大幅に高速に処理が実行されます。

データ型の変換の回避

TimesTenの命令パスは非常に短いため、データ変換による短時間の遅延によっても、トランザクション時間が大幅に増加する可能性があります。

基礎となるデータベース内のデータのデータ型に対しては、デフォルトのResultSet.getXXXメソッドを使用します。たとえば、データのデータ型がDOUBLEである場合、JDBCドライバでのデータ変換を避けるために、getDoubleをコールする必要があります。特定のデータ型に対するデフォルトのgetXXXメソッドの詳細は、『JDBC Database Access with Java』(Hamilton、Cattell、Fisher)のJDBCクリック・リファレンスの項を参照してください。同様に、SQL文の入力パラメータに対しては、デフォルトのPreparedStatement.setXXXメソッドを使用します。たとえば、PreparedStatementを使用してCHAR列にデータを挿入する場合は、setStringを使用する必要があります。

トランザクション・ロールバックの回避

誤ったデータまたはアプリケーション障害のためトランザクションに問題が発生した場合、トランザクションはTimesTenによって自動的にロールバックされます。また、アプリケーションでは、多くの場合、Connection.rollback()を使用してトランザクションのロールバックが明示的に実行され、デッドロックまたはタイムアウトの状態からリカバリされます。パフォーマンスの観点から見ると、これらの処理は望ましくありません。ロールバックはリソースを浪費し、結果的にトランザクション全体が無駄になってしまうためです。

アプリケーションでは、不要なロールバックが行われないようにする必要があります。つまり、可能なかぎり、競合が発生しないようにアプリケーションを設計(「最適なロック方法の選択」を参照)し、送信前にアプリケーションまたは入力データをチェックしてエラーを検出する必要があります。SYS.MONITOR表のXACT_ROLLBACKS列は、ロールバックしたトランザクションの数を示します。

頻繁なチェックポイント処理の回避

長くデータ・ストアに接続されているアプリケーションの場合、ディスクがログ・ファイルで一杯にならないように、ttCkptプロシージャをコールしてデータ・ストアに対してチェックポイント処理を実行する必要があります。トランザクション一貫性チェックポイントは、データ・ストアへの排他アクセスが必要であるため、パフォーマンスに大きな影響を与える可能性があります。

ttCkptBlockingをコールしてブロッキング・チェックポイントを実行するよりも、ttCkptをコールして非ブロッキング(またはファジー)チェックポイントを実行することをお薦めします。非ブロッキング・チェックポイントを使用すると時間がかかりますが、データ・ストアに対して同時に他のトランザクションを実行できるため、オーバーヘッドを少なくできます。ログ・ファイルの蓄積に使用するディスク領域の量を増やすことによって、連続するチェックポイント間の時間隔を長くできます。

ログ・ファイルのサイズが大きくなると(チェックポイント間の間隔が長くなると)、リカバリ時間もそれに応じて長くなります。システム・クラッシュまたはアプリケーション障害からのリカバリ時間を短くする必要がある場合は、チェックポイント処理を頻繁に実行することをお薦めします。SYS.MONITOR表のDS_CHECKPOINTS列は、チェックポイント処理が正常に実行された回数を示します。